<template>
  <div class="svg-container">
    <h2>重庆本地就业热门行业</h2>
    <svg viewBox="0 0 280 280" class="circlesvg"></svg>
  </div>
</template>

<script>
import { hsl } from 'd3-color'
import { max, min } from 'd3-array'
import { select, selectAll } from 'd3-selection'
import { drag } from 'd3-drag'
import {
  forceSimulation,
  forceCenter,
  forceManyBody,
  forceCollide,
  forceX,
  forceY
} from 'd3-force'

export default {
  name: 'CirclePack',
  props: {
    yKey: String,
    width: Number,
    height: Number,
    nodes: Array
  },
  mounted () {
    this.createSimulation()
    this.addNodes(this.$data.nodesComputed)
    this.simulate(this.$data.nodesComputed)
  },

  data: () => ({
    tau: 2 * Math.PI,
    simulation: null,
    nodesComputed: null
  }),
  methods: {

    createSimulation () {
      this.nodesComputed = this.nodes
      this.simulation = forceSimulation()
        .force(
          'forceX',
          forceX()
            .strength(0.3)
            .x(this.width * 0.5)
        )
        .force(
          'forceY',
          forceY()
            .strength(0.3)
            .y(this.height * 0.5)
        )
        .force(
          'center',
          forceCenter()
            .x(this.width * 0.5)
            .y(this.height * 0.5)
        )
        .force('charge', forceManyBody().strength(-15))
      console.log('SIMULATION CREATED')
    },
    dragStarted (event, d) {
      this.hideTooltip()
      if (!event.active) this.simulation.alphaTarget(0.03).restart()
      selectAll('g.bubble').raise()
    },
    dragged (event, d) {
      this.hideTooltip()
      selectAll('g.bubble')
        .attr('cx', (d.x = event.x))
        .attr('cy', (d.y = event.y))
    },
    dragEnded (event, d) {
      if (!event.active) this.simulation.alphaTarget(0.03)
    },
    showTooltip (event, d) {
      this.tooltip
        .style('opacity', 1)
        .html(d.industry)
        .style('left', event.x + 10 + 'px')
        .style('top', event.y + 20 + 'px')
    },
    moveTooltip () {
      this.tooltip
        .style('left', event.x + 10 + 'px')
        .style('top', event.y + 20 + 'px')
    },
    hideTooltip () {
      this.tooltip.style('opacity', 0)
    },
    addNodes (input) {
      const self = this
      this.tooltip = select('.svg-container')
        .append('div')
        .attr('class', 'tooltip')
        .style('opacity', 0)
        .attr('class', 'tooltip')
        .style('background-color', 'black')
        .style('border-radius', '5px')
        .style('padding', '10px')
        .style('color', 'white')

      const chart = select('.circlesvg').append('g')

      const bubbles = chart
        .selectAll('g.bubble')
        .data(input)
        .enter()
        .append('g')
        .append('g')
        .attr('class', 'bubble')
        .attr(
          'transform',
          (d) => `translate(${this.width / 2}, ${this.height / 2})`
        )
        .on('mouseover', self.showTooltip)
        .on('mousemove', self.moveTooltip)
        .on('mouseleave', self.hideTooltip)
        .call(
          drag()
            .on('start', self.dragStarted)
            .on('drag', self.dragged)
            .on('end', self.dragEnded)
        )

      // TODO: 圆大小和圆的颜色
      bubbles
        .append('circle')
        .attr('r', (d) => {
          return d[this.yKey]
        })
        .attr('fill', (d) => {
          return hsl(150, this.normalize(d[this.yKey]), 0.4)
        })
        .attr('cx', (d) => {
          return d.x
        })
        .attr('cy', (d) => {
          return d.y
        })
        .attr('class', 'node-main')

      // TODO: 字体大小
      bubbles
        .append('text')
        .text((d) => d.industry)
        .attr('font-size', (d) => {
          return d.r * 0.4 + 'px'
        })
        .attr('y', (d) => d.y)
        .attr('fill', '#fff')
        .attr('text-anchor', 'middle')
        .attr('dominant-baseline', 'middle')
    },
    simulate (input) {
      this.simulation
        .nodes(input)
        .force(
          'collide',
          forceCollide()
            .strength(0.5)
            .radius((d) => {
              return d.r + 2.5
            })
            .iterations(1)
        )
        .on('tick', (d) => {
          selectAll('.bubble').attr(
            'transform',
            (d) => `translate(${[d.x, d.y]})`
          )
        })
    },
    reconfigure () {
      const self = this
      this.nodesComputed.map((node) => {
        node.x = (self.width / 2) * Math.random()
        node.y = (self.height / 2) * Math.random()
      })
    },
    normalize (v) {
      return (v - this.dataMin) / (this.dataMax - this.dataMin)
    }
  },
  computed: {
    dataMax () {
      return max(this.nodes, (d) => {
        return d[this.yKey]
      })
    },
    dataMin () {
      return min(this.nodes, (d) => {
        return d[this.yKey]
      })
    }
  }
}
</script>

<style scoped>
  h2 {
    position: absolute;
    top: 0;
    width: 100%;
    text-align: center;
    font-size: 35px;
    top: 5%;
  }

  .svg-container {
    display: inline-block;
    position: relative;
    width: 100%;
    height: 100%;
    vertical-align: top;
    overflow: hidden;
    position: relative;
  }
  /deep/ .tooltip {
    position: fixed;
    z-index: 9999;
  }

  /deep/ .bubble:hover {
    cursor: move;
  }

  /deep/ .bubble:hover .node-main {
    stroke: #fff;
    stroke-width: 2px;
  }
  .circlesvg {
    width: 100%;
    height: 100%;
  }
</style>
